/**
 *  WordItem.java : IceBird project
 *  15:11:56 
 */
package icebird.compiler.x86.old;

import icebird.metadata.BasicType;
import icebird.compiler.x86.old.X86Register.GPR;

/**
 * @author Sergey Shulepoff[Knott]
 */
public abstract class WordItem extends Item {

	private GPR gpr;
	
	/**
	 * @param owner
	 */
	public WordItem(ItemFactory owner) {
		super(owner);
	}
	
	/**
	 * Init this word item.
	 * @return void
	 */
	protected final void init(EmitterContext c, byte k, X86Register reg, short bp) {
		super.init(k, bp, (reg instanceof X86Register.XMM) ? ((X86Register.XMM)reg) : null);
		
		gpr = (reg instanceof X86Register.GPR) ? (X86Register.GPR)reg : null;
	}
	
	/*
	 * (non-Javadoc)
	 * @see icebird.compiler.x86.Item#clone(icebird.compiler.x86.EmitterContext)
	 */
	public Item clone(EmitterContext c) {
		X86Assembler asm = c.getAsm();
		WordItem item;
		
		switch (getKind()) {
		case GPR:
			item = CompilerHelper.requestWordReg(c, getType());
			GPR r = item.gpr;
			asm.move(r, gpr);
			break;

		default:
			throw new IllegalStateException();
		}
		
		return item;
	}
	

	/**
	 * @return the gpr
	 */
	public final GPR getRegister() {
		return gpr;
	}

	/* (non-Javadoc)
	 * @see icebird.compiler.x86.Item#load(icebird.compiler.x86.EmitterContext)
	 */
	public final void load(EmitterContext c) {
		if (!isGPR()) {
			X86Register r = c.getGPRPool().request(getType());
			if (r == null) {
				VirtualStack s = c.getVStack();
				s.push(c);
				r = c.getGPRPool().request(getType());
			}
			
			
		}
	}
	
	/**
	 * Load to register.
	 * @return void
	 */
	public final void loadTo(EmitterContext c, GPR reg) {
		X86Assembler asm = c.getAsm();
		VirtualStack stack = c.getVStack();
		
		switch (getKind()) {
		case GPR:
			if (gpr != reg) {
				asm.move(reg, gpr);
				cleanup(c);
			}
			break;
			
		case LOCAL:
			asm.move(reg, X86Register.EBP, bpOffset());
			break;
			
		case CONSTANT:
			loadToConstant(c, asm, reg);
			break;
			
		case FPUSTACK:
			FPUHelper.fxch(asm, stack.getFpuStack(), this);
			stack.getFpuStack().pop(this);
			
			asm.lea(X86Register.ESP, X86Register.ESP, -4); // -4 = slot size(Negative)

			popFromFPU(asm, X86Register.ESP, 0);			
			asm.pop(reg);
			
			break;
			
		case STACK:
			asm.pop(reg);
			break;
			
		default:
			throw new RuntimeException("Unknown kind");
		}
		
		setKind(GPR);
		gpr = reg;
	}
	
	/* (non-Javadoc)
	 * @see icebird.compiler.x86.Item#loadGPR(icebird.compiler.x86.EmitterContext)
	 */
	public final void loadGPR(EmitterContext c) {
		if (isGPR()) return;
		
		X86Register reg = c.getGPRPool().request(BasicType.Int);
		if (reg == null) {
			c.getVStack().push(c);
			reg = c.getGPRPool().request(BasicType.Int);
		}
		
		loadTo(c, (GPR)reg);
	}
	
	

	/* (non-Javadoc)
	 * @see icebird.compiler.x86.Item#push(icebird.compiler.x86.EmitterContext)
	 */
	public void push(EmitterContext c) {
		X86Assembler asm = c.getAsm();
		VirtualStack s = c.getVStack();
		
		switch (getKind()) {
		case GPR:
			asm.push(gpr);
			break;
			
		case LOCAL:
			asm.push(X86Register.EBP, bpOffset());
			break;
			
		case FPUSTACK:
			FPUStack fps = s.getFpuStack();
			FPUHelper.fxch(asm, fps, this);
			fps.pop(this);
			
			asm.lea(X86Register.ESP, X86Register.ESP, -4);
			popFromFPU(asm, X86Register.ESP, 0);
			break;
			
		case CONSTANT:
			pushConstant(c, asm);
			break;
			
		case STACK: break;
		
		default:
			throw new IllegalArgumentException("Invalid item kind");
		}
		
		cleanup(c);
		setKind(STACK);
	}
	
	public final void pushFPU(EmitterContext c) {
		X86Assembler asm = c.getAsm();
		VirtualStack s = c.getVStack();
		
		switch (getKind()) {
		case GPR:
			asm.push(gpr);
			pushToFPU(asm, X86Register.ESP, 0);
			asm.lea(X86Register.ESP, X86Register.ESP, 4);
			break;
			
		case LOCAL:
			pushToFPU(asm, X86Register.EBP, bpOffset());
			break;
			
		case CONSTANT:
			pushConstant(c, asm);
			pushToFPU(asm, X86Register.ESP, 0);
			asm.lea(X86Register.ESP, X86Register.ESP, 4);
			break;
			
		case FPUSTACK:
			s.getFpuStack().pop(this);
			s.getFpuStack().push(this);
			break;
			
		case STACK:
			pushToFPU(asm, X86Register.ESP, 0);
			asm.lea(X86Register.ESP, X86Register.ESP, 4);
			break;
			
		default:
			throw new IllegalArgumentException("Invalid item kind");
		}
		
		cleanup(c);
		setKind(FPUSTACK);
		s.getFpuStack().push(this);
	}
	
	/**
	 * Push my constant on the stack using the given os.
	 * 
	 * @param os
	 */
	protected abstract void pushConstant(EmitterContext ec, X86Assembler os);

	/**
	 * Pop the top of the FPU stack into the given memory location.
	 * 
	 * @param os
	 * @param reg
	 * @param disp
	 */
	protected abstract void popFromFPU(X86Assembler os, GPR reg, int disp);
	
	/**
	 * Push the value at the given memory location on the FPU stack.
	 * 
	 * @param os
	 * @param reg
	 * @param disp
	 */
	protected abstract void pushToFPU(X86Assembler os, GPR reg, int disp);
	
	/**
	 * Create a clone of this item, which must be a constant.
	 * 
	 * @return The cloned item
	 */
	protected abstract WordItem cloneConstant(EmitterContext ec);
	
	/**
	 * Load my constant to the given os.
	 * 
	 * @param os
	 * @param reg
	 */
	protected abstract void loadToConstant(EmitterContext ec, X86Assembler os,
			GPR reg);
	
	private void cleanup(EmitterContext c) {
		if (getKind() == GPR) {
			c.getGPRPool().release(gpr);
		}
		
		gpr = null;
	}

	/* (non-Javadoc)
	 * @see icebird.compiler.x86.Item#release(icebird.compiler.x86.EmitterContext)
	 */
	@Override
	public final void release(EmitterContext c) {
		cleanup(c);
	}

	/* (non-Javadoc)
	 * @see icebird.compiler.x86.Item#spill(icebird.compiler.x86.EmitterContext, icebird.compiler.x86.X86Register)
	 */
	@Override
	public final void spill(EmitterContext c, X86Register r) {
		X86Register reg = c.getGPRPool().request(getType());
		if (reg == null) {
			c.getVStack().push(c);
			if (getKind() == STACK) return;
			
			reg = c.getGPRPool().request(getType());
		}
		
		loadTo(c, (GPR)reg);
	}

	/* (non-Javadoc)
	 * @see icebird.compiler.x86.Item#uses(icebird.compiler.x86.X86Register)
	 */
	public final boolean uses(X86Register reg) {
		return (isGPR() && (gpr.equals(reg)));
	}

	/* (non-Javadoc)
	 * @see icebird.compiler.x86.Item#usesVolatileRegister(icebird.compiler.x86.X86RegisterPool)
	 */
	public final boolean usesVolatileRegister(X86RegisterPool pool) {
		throw new IllegalStateException();
	}
}
